package com.k8.common.ioc.injectorImpl;

import com.k8.common.aware.BeanFactoryAware;
import com.k8.common.factroy.BeanFactory;
import com.k8.common.factroy.instance.InstanceStrategyFactory;
import com.k8.common.ioc.GetOrRegistry;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

import static com.k8.common.factroy.instance.AbstractBeanInstanceStrategyFactory.FIRST_BEAN_SUFFIX;

/*
 * 暂时不提供接口依赖注入，如果要实现，可以通过BeanFactory保存一份Interface-实现类的映射表
 * */
public class BeanFactoryInstanceStrategyFactoryInjector extends AbstractInstanceStrategyFactoryInjector implements BeanFactoryAware {
    BeanFactory beanFactory;

    @Override
    public void synJudge(Map<String, Object> relyBeanNames) {
        InstanceStrategyFactory instanceStrategyFactory = beanFactory.getInstanceStrategyFactory();
        synJudge0(relyBeanNames, instanceStrategyFactory);
    }

    @Override
    public boolean inject(Map<String, Object> relyBeanMaps, Object instance, Field field) {
        try {
            if (field.getAnnotation(Resource.class) != null) {
                Resource annotation = field.getAnnotation(Resource.class);
                GetOrRegistry registry = field.getAnnotation(GetOrRegistry.class);
                String name = annotation.name();
                Class<?> type = field.getType();
                Class<?> clazz = instance.getClass();
                if (type.isInterface()) return false;
                Object property = null;
                if (registry != null) {
                    //如果没有则会创建一个
                    if (name.isEmpty()) {
                        property = beanFactory.getBeanOrRegistryToInject(type);
                        name = getName(clazz);
                    } else {
                        property = beanFactory.getBeanOrRegistryToInject(type, name);
                    }
                } else {
                    //没有则不会创建
                    if (name.isEmpty()) {
                        property = beanFactory.getBean(type);
                        name = getName(clazz);
                    } else {
                        property = beanFactory.getBean(type, name);
                    }
                }
                field.setAccessible(true);
                field.set(instance, property);
                relyBeanMaps.put(name, property);
                return true;
            }
            return false;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private String getName(Class<?> clazz) {
        return clazz.getName() + FIRST_BEAN_SUFFIX;
    }

    @Override
    public void beanFactoryAware(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    public boolean needInject(Field field) {
        return field.isAnnotationPresent(Resource.class) && !field.getType().isInterface();
    }

    private <T> T getBean(final Map<String, SafeObjectHolder> relyBeanMaps, Class<T> clazz, String name, boolean registry) {
        if (clazz.isInterface()) return null;
        T bean = null;
        if (registry) {
            bean = beanFactory.getBeanOrRegistryToInject(clazz, name);
        } else {
            bean = beanFactory.getBean(clazz, name);
        }
        if (bean != null) relyBeanMaps.put(name, new SafeObjectHolder(bean));
        return bean;

    }

    @Override
    public <T> T getBean(final Map<String, SafeObjectHolder> relyBeanMaps, Class<T> clazz, String name) {
        return getBean(relyBeanMaps, clazz, name, false);
    }

    @Override
    public <T> T getBean(final Map<String, SafeObjectHolder> relyBeanMaps, Class<T> clazz) {
        return getBean(relyBeanMaps, clazz, getName(clazz), false);
    }

    @Override
    public <T> T getBeanOrRegistry(final Map<String, SafeObjectHolder> relyBeanMaps, Class<T> clazz, String name) {
        return getBean(relyBeanMaps, clazz, name, true);
    }

    @Override
    public <T> T getBeanOrRegistry(final Map<String, SafeObjectHolder> relyBeanMaps, Class<T> clazz) {
        return getBean(relyBeanMaps, clazz, getName(clazz), true);
    }

    @Override
    public <T> List<T> getAllBeans(Map<String, SafeObjectHolder> relyBeanMaps, Class<T> clazz) {
        return beanFactory.getAllBeans(clazz);
    }

}
